#! /usr/bin/env python
#coding:utf-8

# alert check daemon
import sys
import os
import re
import json
import redis
import time
import urllib
import urllib2
import traceback
from datetime import datetime, date, timedelta
from alert_daemon import client_send
from apscheduler.schedulers.blocking import BlockingScheduler

reload(sys)
sys.setdefaultencoding('utf8')
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from config.tdm_config import *
import config.tdm_mongo_modules as tmm
from acard.acard_utils import *


def tdm_log(mod, city, venue, dev, item, content):
    #print 'in tdm_log content:', content
    mod_obj = tmm.TdmModule.objects(name=mod).first()
    city_obj = tmm.City.objects(name=city).first()
    venue_obj = tmm.Venue.objects(name=venue).first()
    if None in (mod_obj, city_obj, venue_obj):
        print "[log-error] mod:%s, city:%s, venue:%s" % (mod_obj, city_obj, venue_obj)
        return

    log = tmm.TdmLog(tm=datetime.now(), module=mod_obj, city=city_obj, venue=venue_obj, dev=dev, item=item, content=content)
    log.save()
    #print log.to_json()

def tdm_warn_alert(warn_alert):
    def _tdm_warn_alert(fn):
        def __tdm_warn_alert(r, mod, city, venue, dev, item, tm, content):
            rkey = ('%s:%s:%s:%s:%s:%s' % (warn_alert, mod, city, venue, dev, item)).encode('utf8')
            if tm == 0:
                if r.exists(rkey):
                    r.delete(rkey)          # 在redis中删除 warn/alert key
                    #tdm_log(mod, city, venue, dev, item, content)      # 上线暂时不记录日志
            else:
                save_log = r.get(rkey)
                #print 'rkey:', rkey
                #print 'save_log:', save_log
                last_tm, alert_nr, _cont = '0', '0', ''
                if save_log:
                    #print 'save_log:[%s]' % save_log
                    last_tm, alert_nr, _cont = save_log.split(',')

                #print int(time.time())
                #print int(last_tm)
                #print int(time.time())-int(last_tm), TDM_ALERT_RESEND

                if int(time.time())-int(last_tm) > TDM_ALERT_RESEND:    # 超过24小时则重发报警，并累计次数

                    # 超过24小时则重新设置redis的warn/alert
                    rval = '%s,%d,%s' % (tm, int(alert_nr)+1, content)
                    #print 'rkey:', rkey, [rkey]
                    #print 'rval:', rval, [rval]
                    r.set(rkey, rval, ex=TDM_ALERT_ALIVE_EX)            # 超过25小时则报警失效
                    content = '%s, 第%d次' % (content, int(alert_nr)+1)
                    tdm_log(mod, city, venue, dev, item, content)       # 记录日志和发送报警邮件
                    fn(r, mod, city, venue, dev, item, tm, content)

        return __tdm_warn_alert
    return _tdm_warn_alert

@tdm_warn_alert('warn')
def tdm_warn(r, mod, city, venue, dev, item, tm, content):
    pass

@tdm_warn_alert('alert')
def tdm_alert(r, mod, city, venue, dev, item, tm, content):
    module_obj = tmm.TdmModule.objects(name=mod).first()
    city_obj = tmm.City.objects(name=city).first()
    if module_obj.alert_enable:
        alert_subject = '[%s] %s分校-%s 报警' % (module_obj.alias, city, venue)
        alert_content = '%s %s分校-%s-%s %s %s' % (datetime.fromtimestamp(tm).strftime('%Y/%m/%d %H:%M:%S'), city, venue, dev, item, content)
        if module_obj.alert_by_city:
            mail_list = [u.email for u in module_obj.officers if city_obj in u.station]
            phones_list = ','.join([u.phone for u in module_obj.officers if u.phone and city_obj in u.station])
        else:
            mail_list = [u.email for u in module_obj.officers]
            phones_list = ','.join([u.phone for u in module_obj.officers if u.phone])
        mail = {
            'to_addr': mail_list,
            'subject': alert_subject.encode('utf-8'),
            'content': alert_content.encode('utf-8'),
        }
        alert_cfg = tmm.TdmAlert.objects().first()
        login = {
            'user': alert_cfg.alert_mail_user,
            'passwd': alert_cfg.alert_mail_pass
        }
        client_send(login, mail, phones_list)


def acard_alert_check_ctm_offline(r, city, venue, ctm, key, val):
    if not val:
        print '[offline]', datetime.now(), key
        # 补刀空缺中控机日志，status=False
        last_tm = curr_tm = datetime.now()
        last_log = tmm.AcardCtmLog.objects(ctm=ctm).order_by('-log_tm').first()
        if not last_log:
            return
        vsl = None
        while (curr_tm - last_tm).total_seconds() >= 0:
            vsl = tmm.AcardCtmLog(city=city, venue=venue, ctm=ctm, log_tm=last_tm+timedelta(0, 300, 0))
            vsl.save()
            last_tm = last_tm + timedelta(0, 300, 0)
        if vsl:
            AcardCtmLog_mongo_to_redis(r, vsl, key, redis_venue_sync_log_ex)
        tdm_alert(r, 'acard', city.name, venue.name, ctm.memo, 'ctm-online', int(time.time()), '电子考勤中控机掉线')
    # 未超时
    else:
        ctm_dict = json.loads(val)
        last_tm = datetime.strptime(ctm_dict['log_tm'], '%Y-%m-%dT%H:%M:%S')
        curr_tm = datetime.now()
        # 最后一次更新时间超过当前时间15分钟
        if (curr_tm - last_tm).total_seconds() > 900:
            print '[offline]', datetime.now(), key
            # 补刀一个中控机日志，status=False，日志时间为上次时间+300秒
            while (curr_tm - last_tm).total_seconds() >= 0:
                vsl = tmm.AcardCtmLog(city=city, venue=venue, ctm=ctm, log_tm=last_tm+timedelta(0, 300, 0))
                vsl.save()
                last_tm = last_tm + timedelta(0, 300, 0)
            AcardCtmLog_mongo_to_redis(r, vsl, key, redis_venue_sync_log_ex)
            tdm_alert(r, 'acard', city.name, venue.name, ctm.memo, 'ctm-online', int(time.time()), '电子考勤中控机掉线')
        elif ctm_dict['status'] == True:
            #print '[online]', datetime.now(), key
            # clear the warn/alert
            tdm_alert(r, 'acard', city.name, venue.name, ctm.memo, 'ctm-online', 0, '电子考勤中控机上线')

def acard_alert_check_cpu_mem_disk(r, city, venue, ctm, key, val):
    if not val:
        return
    ctm_dict = json.loads(val)
    cpu = ctm_dict.get('cpu', 0.0)
    mem = ctm_dict.get('memory', 0.0)
    disk = ctm_dict.get('disk', 0.0)
    if cpu > 80:
        tdm_alert(r, 'acard', city.name, venue.name, ctm.devid, 'cpu', int(time.time()), 'CPU超负荷运行%.2f%%' % cpu)
        print '[cpu-alert]', datetime.now(), key
    else:
        tdm_alert(r, 'acard', city.name, venue.name, ctm.devid, 'cpu', 0, 'CPU正常运行%.2f%%' % cpu)
    if mem > 80:
        tdm_alert(r, 'acard', city.name, venue.name, ctm.devid, 'mem', int(time.time()), '内存空间不足%.2f%%' % mem)
        print '[mem-alert]', datetime.now(), key
    else:
        tdm_alert(r, 'acard', city.name, venue.name, ctm.devid, 'mem', 0, '内存正常运行%.2f%%' % mem)
    if disk > 80:
        tdm_alert(r, 'acard', city.name, venue.name, ctm.devid, 'disk', int(time.time()), '磁盘空间不足%.2f%%' % disk)
        print '[disk-alert]', datetime.now(), key
    else:
        tdm_alert(r, 'acard', city.name, venue.name, ctm.devid, 'disk', 0, '磁盘正常运行%.2f%%' % disk)

def acard_alert_check_ip_100(r, city, venue, ctm, key, val):
    if not val:
        return
    ctm_dict = json.loads(val)
    ip_100 = ctm_dict.get('ip_100')
    if not ip_100:
        tdm_alert(r, 'acard', city.name, venue.name, ctm.devid, 'ip-100', int(time.time()), '主备IP掉线')
        print '[ip_100-alert]', datetime.now(), key
    else:
        tdm_alert(r, 'acard', city.name, venue.name, ctm.devid, 'ip-100', 0, '主备IP上线')

def acard_alert_check_crstatus(r, city, venue, ctm, key, val):
    #"crstatus": [{"status": true, "ip": "192.168.13.101"}, {"status": true, "ip": "192.168.13.102"}],
    if not val:
        return
    ctm_dict = json.loads(val)
    crstatus = ctm_dict.get('crstatus')
    if crstatus and ctm.devid.find('_dm_') > 0:
        for crs in crstatus:
            if not crs['status']:
                tdm_warn(r, 'acard', city.name, venue.name, crs['ip'], 'crstatus', int(time.time()), '读卡器故障')
                print '[crstatus-warn]', datetime.now(), key, crs['ip']
            else:
                tdm_warn(r, 'acard', city.name, venue.name, crs['ip'], 'crstatus', 0, '读卡器恢复')


def get_attend_from_yewu(zipcode):
    yewu_url = tdm_attend_from_yewu_url % (zipcode)
    rsp_obj = {}
    print 'get_attend_from_yewu attend:', zipcode
    try:
        req = urllib2.Request(yewu_url)
        rsp = urllib2.urlopen(req, timeout=5)
        '''
        { 
            "rlt": true,
            "msg": "",
            "procedure_before_count": 600,
            "procedure_after_count": 600
        }
        '''
        rsp_obj = json.loads(rsp.read())
    except Exception as e:
        print "error:", str(e)
        return 0
    if rsp_obj.get('rlt'):
        return rsp_obj.get('procedure_before_count', 0)
    else:
        print rsp_obj.get('msg')
        return 0

def get_attend_from_register(zipcode):
    headers = {
        'area': zipcode
    }
    values = {
        'startTime': date.today().strftime('%Y-%m-%d %H:%M:%S'),
        'endTime': (date.today()+timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')
    }

    rsp_obj = {}
    try:
        datas = urllib.urlencode(values)
        req = urllib2.Request(tdm_attend_from_register_url, datas, headers)
        rsp = urllib2.urlopen(req, timeout=30)
        rsp_obj = json.loads(rsp.read())
    except Exception as e:
        print "error:", str(e)

    if rsp_obj.get("rlt"):
        return rsp_obj.get('data', 0)
    else:
        print rsp_obj.get('msg', 'get_attend_from_register error')
        return 0

def get_attend_from_core(zipcode, date):
    _data = {}
    _data['areaCode'] = zipcode
    _data['classDate'] = date
    data = urllib.urlencode(_data)

    rsp_obj = {}
    try:
        req = urllib2.Request(tdm_attend_from_core_url, data)
        rsp = urllib2.urlopen(req, timeout=5)
        rsp_obj = json.loads(rsp.read())
    except Exception as e:
        print "error:", str(e)

    city_venue_class = rsp_obj.get('date')
    if not city_venue_class:
        return []
    '''
    [[u'ff808081459772e70145ac5796bf76b7', 34], [u'ff80808149bf20610149c0f0ad340114', 102]]
    '''
    return city_venue_class

def acard_alert_check(r, dt):
    city_venue_class = {}
    city_all = tmm.City.objects.all()
    for city in city_all:
        # 获取业务系统分校当天考勤数据
        attend_data_yewu = acard_get_data_yewu(r, city)

        # 获取考勤系统core分校当天考勤数据
        _city_core_class = acard_get_venue_datalist_core(r, city)
        attend_data_core = acard_get_data_core(r, city)
        city_venue_class[city.zipcode] = dict(_city_core_class)

        # 获取考勤客户端分校当天数据
        attend_data_ctm = acard_get_data_ctm(r, city)

        # 教学点检查
        for venue in city.venues:
            # 教学点被禁用
            if venue.disabled:
                print '=== DISABLED === %s(%s)' % (city.name, venue.name)
                continue
            # 教学点今日无课
            if not city_venue_class[city.zipcode].has_key(venue.venue_id):
                print '=== No course today === %s(%s)' % (city.name, venue.name)
                continue
            for ctm in venue.ctm_devs:
                acard_ctm_key = '%s:%s:%s:%s' % (redis_venue_sync_log_prefix, city.name, venue.name, ctm.devid)
                acard_ctm_val = r.get(acard_ctm_key)
                # ctm offline check
                acard_alert_check_ctm_offline(r, city, venue, ctm, acard_ctm_key, acard_ctm_val)
                # cardreader online and server config check
                acard_alert_check_crstatus(r, city, venue, ctm, acard_ctm_key, acard_ctm_val)


def attend_turnout(dt):
    if dt.hour < 21:
        return
    city_all = tmm.City.objects.all()
    for city in city_all:
        for venue in city.venues:
            if venue.disabled:
                continue
            for ctm in venue.ctm_devs:
                print ctm.devid
                last_log = tmm.AcardCtmLog.objects(ctm=ctm).order_by('-log_tm').first()
                if not last_log:
                    continue
                attout = tmm.AttendTurnoutLog.objects(city=city, venue=venue, ctm=ctm, date=last_log.log_tm.date()).first()
                if not attout:
                    attout = tmm.AttendTurnoutLog(city=city, venue=venue, ctm=ctm, date=last_log.log_tm.date())
                attout.attend = last_log.attend
                attout.noattend = last_log.noattend
                attout.toattend = last_log.toattend
                attout.save()

def acard_alert_clear(r):
    for i in r.keys('alert:acard:*'):
        print '[clean]', i
        r.delete(i)
    for i in r.keys('warn:acard:*'):
        print '[clean]', i
        r.delete(i)

def attend_data_cache_clear(r):
    for i in r.keys('data:*'):
        print '[clean]', i
        r.delete(i)

if __name__ == '__main__':
    r = redis.Redis(host=tdm_redis_host, db=tdm_redis_db)
    tmm.db_connect(tdm_mongo_acard_db)
    #acard_alert_clear(r)
    attend_data_cache_clear(r)

    while True:
        dt = datetime.now()
        if dt.hour < 6 or dt.hour >= 22:
            print dt, 'sleeping...'
            time.sleep(60)
            continue
        try:
            print dt
            acard_alert_check(r, dt)
            attend_turnout(dt)
        except Exception as e:
            print "alert_check error:", str(e)
            traceback.print_exc()
        time.sleep(60)

